package com.ponhu.ea2.goodsCenter.provider.utils;

import com.ponhu.ea2.vo.PhPropertyVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.core.io.Resource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import sun.nio.ch.FileChannelImpl;

import java.io.*;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.nio.MappedByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author luyu
 * @create 2021-10-27 14:22
 * 一些公共的使用方法
 */
@Slf4j
public class CommonUtils {

    private static final RestTemplate restTemplate = new RestTemplate();

    static {
        restTemplate.setInterceptors(new ArrayList<>());
        CloseableHttpClient httpClient = HttpClientBuilder.create()
                // maxConnTotal是同时间正在使用的最多的连接数
                .setMaxConnTotal(500)
                // maxConnPerRoute是针对一个域名同时间正在使用的最多的连接数
                .setMaxConnPerRoute(500)
                .build();
        HttpComponentsClientHttpRequestFactory httpRequestFactory =
                new HttpComponentsClientHttpRequestFactory(httpClient);
        // 高并发下等待链接超时
        httpRequestFactory.setConnectionRequestTimeout(60000);
        httpRequestFactory.setConnectTimeout(60000);
        // 成功建立链接后的读取超时
        httpRequestFactory.setReadTimeout(15000);
        restTemplate.setRequestFactory(httpRequestFactory);
    }

    /**
     * 物料编码类型
     */
    private static final String MATERIAL_CODE_TYPE = "10";

    /**
     * 获取生成的物料编码
     *
     * @param materialType    物料类型(2位字符串类型)
     * @param thirdCategoryId
     * @return
     */
    public static String createAndGetMaterialCode(String materialType, Long thirdCategoryId) {
        String categoryId = String.format("%03d", thirdCategoryId);
        String datetime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String randomNumber = String.format("%06d", RandomUtils.nextInt(0, 1000000));

        return String.format("%s%s%s%s", materialType, categoryId, datetime, randomNumber);
    }

    /**
     * 获取生成的物料编码
     *
     * @return
     */
    public static String createAndGetMaterialCode(Long thirdCategoryId, ConcurrentHashMap<String, AtomicInteger> hashMap) {
        String datetime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String key = String.format("%s-%s", thirdCategoryId, datetime).intern();
        AtomicInteger atomicInteger = hashMap.get(key);
        if (ObjectUtils.isEmpty(atomicInteger)) {
            synchronized (key) {
                atomicInteger = hashMap.get(key);
                if (ObjectUtils.isEmpty(atomicInteger)) {
                    hashMap.put(key, new AtomicInteger(0));
                    atomicInteger = hashMap.get(key);
                }
            }
        }

        String categoryId = String.format("%03d", thirdCategoryId);
        String autoNumber = String.format("%06d", atomicInteger.getAndIncrement());
        return String.format("%s%s%s%s", MATERIAL_CODE_TYPE, categoryId, datetime, autoNumber);
    }

    /**
     * 获取生成的物料编码
     *
     * @return
     */
    public static String createAndGetMaterialCode(Long thirdCategoryId) {
        return createAndGetMaterialCode(MATERIAL_CODE_TYPE, thirdCategoryId);
    }

    // 生成商品编码规则 是否自营（1固定）+商品类型（01固定）+年月日+随机六位数
    public static String createGoodsCode() {
        String TimeNow = new SimpleDateFormat("yyyyMMdd").format(Calendar.getInstance().getTime());
        String format = String.format("%06d", new Random().nextInt(999999));
        String goodsCode = "101" + TimeNow + format;
        return goodsCode;
    }

    // 针对多选属性,封装属性名称和属性值,多选属性值按照,分割
    public static List<PhPropertyVo> packagingPhPropertyVo(List<?> tList) {

        List<PhPropertyVo> list = tList.stream()
                .map(item -> ProBeanUtils.copyProperties(item, PhPropertyVo.class))
                .collect(Collectors.toList());

        Map<String, List<PhPropertyVo>> collect = list.stream()
                .collect(Collectors.groupingBy(PhPropertyVo::getPropertyName));

        return collect.entrySet().stream().map(entry -> {
            PhPropertyVo vo = entry.getValue().get(0);
            String values = entry.getValue().stream()
                    .map(PhPropertyVo::getPropertyValue)
                    .collect(Collectors.joining(","));
            return new PhPropertyVo()
                    .setPropertyId(vo.getPropertyId())
                    .setPropertyName(entry.getKey())
                    .setPropertyValue(values);
        }).collect(Collectors.toList());
    }

    /**
     * 对比两个值是否相等
     *
     * @param a
     * @param b
     * @return
     */
    public static boolean deepEquals(Object a, Object b) {
        if (a == b)
            return true;
        else if (a == null || b == null)
            return false;
        else {
            if (a instanceof BigDecimal && b instanceof BigDecimal) {
                return ((BigDecimal) a).compareTo((BigDecimal) b) == 0;
            } else {
                return a.equals(b);
            }
        }
    }

    /**
     * Object对象值转换成String
     *
     * @param obj
     * @return
     */
    public static String convertObjectValue(Object obj) {
        if (obj == null)
            return "";
        if (obj instanceof BigDecimal) {
            return String.format("%.2f", obj);
        } else {
            return obj.toString();
        }
    }

    public static String getDetailCauseMsg(Throwable throwable) {
        Throwable cause = throwable.getCause();
        if (ObjectUtils.isEmpty(cause)) return null;
        StackTraceElement[] stackTrace = cause.getStackTrace();
        if (ObjectUtils.isEmpty(stackTrace) || stackTrace.length == 0) return null;
        if (ObjectUtils.isEmpty(stackTrace[0])) return null;
        return stackTrace[0].toString();
    }

    /**
     * 获取指定图片链接的输入流
     *
     * @param imgUrl
     * @return
     */
    public static InputStream getInputStreamByUrl(String imgUrl) {
        return getInputStreamByUrl(imgUrl, 1);
    }

    /**
     * 获取指定图片链接的输入流
     *
     * @param imgUrl
     * @return
     */
    public static InputStream getInputStreamByUrl(String imgUrl, int retry) {
        try {
            if (StringUtils.isEmpty(imgUrl)) return null;
            Resource resource = restTemplate.getForObject(imgUrl, Resource.class);
            return resource.getInputStream();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取指定图片链接的输入流异常 ===>> retry={} ===>> {} ===>> Error={}", retry, imgUrl, e.getMessage());
            if (retry >= 10) return null;
            return getInputStreamByUrl(imgUrl, ++retry);
        }
    }

    /**
     * 使用Map映射文件进行文件压缩
     */
    public static void zipFileMap(String targetPath, String zipPath) {
        FileOutputStream fileOutputStream = null;
        ZipOutputStream zipOut = null;
        WritableByteChannel writableByteChannel = null;
        try {
            File zipFile = new File(zipPath);
            fileOutputStream = new FileOutputStream(zipFile);
            zipOut = new ZipOutputStream(fileOutputStream);
            writableByteChannel = Channels.newChannel(zipOut);
            zipProcess(zipOut, writableByteChannel, new File(targetPath), "");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writableByteChannel != null) {
                try {
                    writableByteChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (zipOut != null) {
                try {
                    zipOut.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 使用Map映射文件进行文件压缩
     */
    private static void zipProcess(ZipOutputStream zipOut, WritableByteChannel writableByteChannel, File targetFile, String base) {
        if (targetFile.isDirectory()) {
            File[] files = targetFile.listFiles();
            base += (base.length() == 0 ? "" : "/");
            for (File file : files) {
                zipProcess(zipOut, writableByteChannel, file, base + file.getName());
            }
        } else {
            RandomAccessFile randomAccessFile = null;
            FileChannel channel = null;
            try {
                zipOut.putNextEntry(new ZipEntry(base));

                randomAccessFile = new RandomAccessFile(targetFile, "r");
                channel = randomAccessFile.getChannel();
                //内存中的映射文件
                MappedByteBuffer mappedByteBuffer = channel
                        .map(FileChannel.MapMode.READ_ONLY, 0, targetFile.length());

                writableByteChannel.write(mappedByteBuffer);

                Method method = FileChannelImpl.class.getDeclaredMethod("unmap", MappedByteBuffer.class);
                method.setAccessible(true);
                method.invoke(FileChannelImpl.class, mappedByteBuffer);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {

                if (randomAccessFile != null) {
                    try {
                        randomAccessFile.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (channel != null) {
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 删除指定路径的文件或文件夹
     *
     * @param path
     */
    public static void delFileOrDirectory(String path) {
        try {
            Files.walkFileTree(Paths.get(path), new FileVisitor<Path>() {
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Files.delete(file);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    Files.deleteIfExists(dir);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
